import * as Cesium from "cesium"
import { ObservableArray } from "../../utils"
import { addAxisToEntity } from "../../utils"

interface Option {
  modelUrl?: string
}

/**
 * @description 相机路径漫游
 *
 */
export class CameraPathRoaming {
  constructor(
    public viewer: Cesium.Viewer,
    public options?: Option,
  ) {
    this.checkPoints.onChange((newVal, oldVal) => {
      this.onCheckPointsChange(newVal, oldVal)
    })
  }
  /*----------------------状态-------------------*/
  cursor: number = 0 //漫游进度
  //检查点列表
  checkPoints: ObservableArray<CameraOption> =
    new ObservableArray<CameraOption>([])

  isRoaming: boolean = false //是否正在漫游
  roamInterval = 2000 //相邻检查点间飞行的事件(ms)
  cameraPath: CameraOption[] = [] //插值生成的相机路径
  pathEntity: Cesium.Entity | undefined
  checkPointEntities: Cesium.Entity[] = []

  /*----------------------公共方法-------------------*/
  //添加检查点
  addCheckPoint(camera: Cesium.Camera) {
    const { position, heading, pitch, roll } = camera
    //从旋转矩阵获取四元数
    const quaternion = Cesium.Quaternion.fromHeadingPitchRoll(
      new Cesium.HeadingPitchRoll(heading, pitch, roll),
    )
    this.checkPoints.value.push({
      position: position.clone(),
      orientation: quaternion,
    })
  }

  //清空检查点
  removeCheckPoints() {
    this.checkPoints.value = []
    this.removePathAndCheckPointEntities()
    this.cameraPath = []
  }

  //开始漫游
  start() {
    if (!this.isRoaming && this.cameraPath.length > 0) {
      //隐藏路径
      this.setPathEntitiesVisible(false)
      this.viewer.scene.postRender.addEventListener(this.renderCallback)
      this.isRoaming = true
    } else {
      console.error("正在漫游中，无法重复开始")
    }
  }

  //结束漫游
  end() {
    if (this.isRoaming) {
      //隐藏路径
      this.setPathEntitiesVisible(true)
      this.viewer.scene.postRender.removeEventListener(this.renderCallback)
      this.isRoaming = false
      this.cursor = 0
    }
  }

  //获取路径
  getPath() {
    return this.checkPoints.value
  }

  //导入路径
  readPath(data: CameraOption[]) {
    this.checkPoints.value = data
  }

  //进度条跳转(小数点)
  jumpTo(percentage: number) {
    this.cursor = Math.floor(this.cameraPath.length * percentage)
  }

  //监听进度(可重写)
  onPlay(percent: number) {}

  //插值相机路径和朝向
  private computeCameraPathAndOrientation(
    checkPoints: CameraOption[],
  ): CameraOption[] {
    if (checkPoints.length < 2) {
      console.error("CatmullRom样条需要至少2个控制点")
      return []
    }

    /*--------------- 位置插值 (CatmullRomSpline) ---------------*/
    const segments = 100 //相邻控制点之间插值的数量
    const spline = new Cesium.CatmullRomSpline({
      times: checkPoints.map((_, i) => i * segments),
      points: checkPoints.map((item) => item.position),
    })
    const positions: Cesium.Cartesian3[] = []
    for (let i = 0; i <= (checkPoints.length - 1) * segments; i++) {
      const newPosition = spline.evaluate(i, new Cesium.Cartesian3())
      positions.push(newPosition)
    }

    //路径可视化
    if (this.pathEntity) this.viewer.entities.remove(this.pathEntity)
    this.pathEntity = this.viewer.entities.add({
      polyline: {
        positions,
        material: new Cesium.PolylineGlowMaterialProperty({
          glowPower: 0.1,
          color: Cesium.Color.YELLOW,
        }),
        width: 10,
      },
    })

    /*--------------- 朝向插值 (QuaternionSpline) ---------------*/
    const quatSpline = new Cesium.QuaternionSpline({
      times: checkPoints.map((_, i) => i * segments), // 与控制点时间参数一致
      points: checkPoints.map((item) => item.orientation),
    })
    const results = positions.map((position, i) => {
      return {
        position,
        orientation: quatSpline.evaluate(i, new Cesium.Quaternion()),
      }
    })
    return results
  }

  //相机视角设置(每帧调用)
  private renderCallback = () => {
    //进度传递
    this.onPlay(this.cursor / this.cameraPath.length)
    //从路径取值
    const option = this.cameraPath[this.cursor++]
    const headingPitchRoll = Cesium.HeadingPitchRoll.fromQuaternion(
      option.orientation,
    )
    //相机飞行
    this.viewer.camera.setView({
      destination: option.position,
      orientation: {
        ...headingPitchRoll,
      },
    })
    if (this.cursor == this.cameraPath.length) {
      this.end()
    }
  }

  //检查点变化时触发
  private onCheckPointsChange(newVal: CameraOption[], oldVal: CameraOption[]) {
    console.log("新值:", newVal, "旧值：", oldVal)
    if (newVal.length > oldVal.length) {
      //找到新增的元素
      const newItems = newVal.filter(
        (newItem) =>
          !oldVal.some(
            (oldItem) => JSON.stringify(oldItem) === JSON.stringify(newItem),
          ),
      )
      newItems.forEach((newItem) => {
        //添加检查点可视化实体
        const option: Cesium.Entity.ConstructorOptions = {
          position: newItem.position,
        }
        if (this.options && this.options.modelUrl) {
          const cameraOrientation = newItem.orientation
          const cameraHpr =
            Cesium.HeadingPitchRoll.fromQuaternion(cameraOrientation)
          const orientation = Cesium.Transforms.headingPitchRollQuaternion(
            newItem.position,
            new Cesium.HeadingPitchRoll(
              cameraHpr.heading + Cesium.Math.toRadians(-90),
              cameraHpr.pitch,
              cameraHpr.roll,
            ),
          )
          option.orientation = orientation
          option.model = {
            uri: this.options.modelUrl,
            minimumPixelSize: 50,
            silhouetteColor: Cesium.Color.YELLOW,
            silhouetteSize: 5,
          }
        } else {
          option.point = {
            pixelSize: 8,
            color: Cesium.Color.TRANSPARENT,
            outlineColor: Cesium.Color.YELLOW,
            outlineWidth: 3,
          }
        }
        const entity = this.viewer.entities.add(option)
        this.checkPointEntities.push(entity)
      })
    }
    if (newVal.length > 2) {
      this.cameraPath = this.computeCameraPathAndOrientation(newVal)
    }
  }

  //移除路径和检查点实体
  private removePathAndCheckPointEntities() {
    this.checkPointEntities &&
      this.checkPointEntities.forEach((item) =>
        this.viewer.entities.remove(item),
      )
    this.pathEntity && this.viewer.entities.remove(this.pathEntity)
  }

  //设置路径和检查点实体显隐
  private setPathEntitiesVisible(visibility: boolean) {
    this.checkPointEntities &&
      this.checkPointEntities.forEach((item) => (item.show = visibility))
    this.pathEntity && (this.pathEntity.show = visibility)
  }
}

interface CameraOption {
  position: Cesium.Cartesian3
  orientation: Cesium.Quaternion
}
